/**
 * Copyright &copy; 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.
 */
package com.dmp.util;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisException;

import java.io.IOException;
import java.util.*;

/**
 * Jedis 读取类JedisReadUtils
 * 说明：这里JedisWriteUtils和JedisReadUtils的方法是一一对应的
 */
public class JedisReadUtils {

    private static Logger logger = LoggerFactory.getLogger(JedisReadUtils.class);

    private static JedisPool pool;


    /**
     * 从文件名获取参数，配置redis连接
     *
     * @param fileName
     */
    public static void setJedisPool(String fileName) {
        if (fileName == null) {
            fileName = "greenet.properties";
        }

        Properties props = new Properties();
        try {
            props.load(JedisReadUtils.class.getClassLoader().getResourceAsStream(fileName));
        } catch (IOException e) {
            logger.error(e.getMessage());
            throw (new JedisException("can not load from file:" + fileName));
        }

        String host = props.getProperty("redis.host");
        int port = Integer.parseInt(props.getProperty("redis.port"));
        int timeout = Integer.parseInt(props.getProperty("redis.timeout"));
        String password = props.getProperty("redis.password");

        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(100);
        config.setMaxIdle(20);
        config.setMaxWaitMillis(1000l);
        config.setTestOnBorrow(false);

        // 线程数量限制，IP地址，端口，超时时间
        if (StrUtils.isNotBlank(password)) {
            //采用有密码登录
            pool = new JedisPool(config, host, port, timeout, password);
        } else {
            pool = new JedisPool(config, host, port, timeout);
        }
    }

    /**
     * 设置redis连接JedisPool
     *
     * @param jedisPool
     */
    public static void setJedisPool(JedisPool jedisPool) {
        if (null == jedisPool) {
            throw (new JedisException("param's jedisPool is null."));
        }

        JedisReadUtils.pool = jedisPool;
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public static String get(String key) {
        String value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            if (jedis.exists(key)) {
                value = jedis.get(key);
                value = StrUtils.isNotBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null;
                logger.debug("get {} = {}", key, value);
            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("get {} = {}", key, value, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public static Object getObject(String key) {
        Object value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            if (jedis.exists(getBytesKey(key))) {
                value = toObject(jedis.get(getBytesKey(key)));
                logger.debug("getObject {} = {}", key, value);
            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("getObject {} = {}", key, value, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }


    /**
     * 获取List缓存
     *
     * @param key 键
     * @return 值
     */
    public static List<String> getList(String key) {
        List<String> value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            if (jedis.exists(key)) {
                value = jedis.lrange(key, 0, -1);
                logger.debug("getList {} = {}", key, value);
            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("getList {} = {}", key, value, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }

    /**
     * 获取List缓存
     *
     * @param key 键
     * @return 值
     */
    public static List<Object> getObjectList(String key) {
        List<Object> value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            if (jedis.exists(getBytesKey(key))) {
                List<byte[]> list = jedis.lrange(getBytesKey(key), 0, -1);
                value = Lists.newArrayList();
                for (byte[] bs : list) {
                    value.add(toObject(bs));
                }
                logger.debug("getObjectList {} = {}", key, value);
            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("getObjectList {} = {}", key, value, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }


    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public static Set<String> getSet(String key) {
        Set<String> value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            if (jedis.exists(key)) {
                value = jedis.smembers(key);
                logger.debug("getSet {} = {}", key, value);
            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("getSet {} = {}", key, value, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public static Set<Object> getObjectSet(String key) {
        Set<Object> value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            if (jedis.exists(getBytesKey(key))) {
                value = Sets.newHashSet();
                Set<byte[]> set = jedis.smembers(getBytesKey(key));
                for (byte[] bs : set) {
                    value.add(toObject(bs));
                }
                logger.debug("getObjectSet {} = {}", key, value);
            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("getObjectSet {} = {}", key, value, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }


    /**
     * 获取HashMap缓存
     *
     * @param key
     * @param field
     * @return
     */
    public static String getStringForMap(String key, String field) {
        String value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        byte[] data = null;
        try {
            jedis = getJedis();
            if (jedis.hexists(getBytesKey(key), getBytesKey(field))) {
                data = jedis.hget(getBytesKey(key), getBytesKey(field));
                if (data == null || data.length < 0) {
                    return null;
                } else {
                    value = new String(data, "UTF-8");
                }

            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("getStringForMap {}", key, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }


    /**
     * 获取HashMap缓存
     *
     * @param key
     * @param field
     * @return
     */
    public static Object getObjectForMap(String key, String field) {
        Object value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        byte[] data = null;
        try {
            jedis = getJedis();
            //判断指定Key中的指定Field是否存在
            if (jedis.hexists(getBytesKey(key), getBytesKey(field))) {
                data = jedis.hget(getBytesKey(key), getBytesKey(field));
                if (data == null || data.length < 0) {
                    return null;
                } else {
                    value = toObject(data);
                }
            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("getObjectForMap {}", key, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }

    /**
     * 获取Map缓存
     *
     * @param key 键
     * @return 值
     */
    public static Map<String, String> getMap(String key) {
        Map<String, String> value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            if (jedis.exists(key)) {
                value = jedis.hgetAll(key);
                logger.debug("getMap {} = {}", key, value);
            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("getMap {} = {}", key, value, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }

    /**
     * 获取Map缓存
     *
     * @param key 键
     * @return 值
     */
    public static Map<String, Object> getObjectMap(String key) {
        Map<String, Object> value = null;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            if (jedis.exists(getBytesKey(key))) {
                value = Maps.newHashMap();
                Map<byte[], byte[]> map = jedis.hgetAll(getBytesKey(key));
                for (Map.Entry<byte[], byte[]> e : map.entrySet()) {
                    value.put(StrUtils.toString(e.getKey()), toObject(e.getValue()));
                }
                logger.debug("getObjectMap {} = {}", key, value);
            }
        } catch (Exception e) {
            isBroken = true;
            logger.warn("getObjectMap {} = {}", key, value, e);
        } finally {
            release(jedis, isBroken);
        }
        return value;
    }


    /**
     * 判断Map缓存中的Key是否存在
     *
     * @param key    键
     * @param mapKey 值
     * @return
     */
    public static boolean mapExists(String key, String mapKey) {
        boolean result = false;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            result = jedis.hexists(key, mapKey);
            logger.debug("mapExists {}  {}", key, mapKey);
        } catch (Exception e) {
            isBroken = true;
            logger.warn("mapExists {}  {}", key, mapKey, e);
        } finally {
            release(jedis, isBroken);
        }
        return result;
    }

    /**
     * 判断Map缓存中的Key是否存在
     *
     * @param key    键
     * @param mapKey 值
     * @return
     */
    public static boolean mapObjectExists(String key, String mapKey) {
        boolean result = false;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            result = jedis.hexists(getBytesKey(key), getBytesKey(mapKey));
            logger.debug("mapObjectExists {}  {}", key, mapKey);
        } catch (Exception e) {
            isBroken = true;
            logger.warn("mapObjectExists {}  {}", key, mapKey, e);
        } finally {
            release(jedis, isBroken);
        }
        return result;
    }


    /**
     * 缓存是否存在
     *
     * @param key 键
     * @return
     */
    public static boolean exists(String key) {
        boolean result = false;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            result = jedis.exists(key);
            logger.debug("exists {}", key);
        } catch (Exception e) {
            isBroken = true;
            logger.warn("exists {}", key, e);
        } finally {
            release(jedis, isBroken);
        }
        return result;
    }

    /**
     * 缓存是否存在
     *
     * @param key 键
     * @return
     */
    public static boolean existsObject(String key) {
        boolean result = false;
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            result = jedis.exists(getBytesKey(key));
            logger.debug("existsObject {}", key);
        } catch (Exception e) {
            isBroken = true;
            logger.warn("existsObject {}", key, e);
        } finally {
            release(jedis, isBroken);
        }
        return result;
    }


    /**************************************************************************************************/
    /**
     * 注意：以下所有都采用DBIndex，如果是集群，DBIndex设置为0
     */
    /**
     * 注意： 作为一个key value存在，很多开发者自然的使用set/get方式来使用Redis，实际上这并不是最优化的使用方法。
     * 尤其在未启用VM情况下，Redis全部数据需要放入内存，节约内存尤其重要。
     * 假如一个key-value单元需要最小占用512字节，即使只存一个字节也占了512字节。
     * 这时候就有一个设计模式，可以把key复用，几个key-value放入一个key中，value再作为一个set存入，
     * 这样同样512字节就会存放10-100倍的容量。 用于存储多个key-value的值，比如可以存储好多的person Object
     * 例子：>redis-cli 存储：redis 127.0.0.1:6379> hset personhash personId
     * personObject 获得：redis 127.0.0.1:6379> hget personhash personId
     * (就可以获得当前personId对应的person对象)
     *
     */
    /**
     * @param DBIndex
     * @param key
     * @param field
     * @return
     */
    public static Object hgetItem(int DBIndex, String key, String field) {
        Jedis jedis = null;
        boolean isBroken = false;
        byte[] data = null;
        Object result = null;
        try {
            jedis = getJedis();
            jedis.connect();
            jedis.select(DBIndex);
            data = jedis.hget(getBytesKey(key), getBytesKey(field));
            if (data.length < 0 || data == null) {
                return null;
            }
            result = toObject(data);
        } catch (Exception e) {
            isBroken = true;
            logger.warn("hgetItem {}", key, e);
        } finally {
            release(jedis, isBroken);
        }
        return result;
    }

    /**
     * 获取指定key的Object
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public Object getItem(int dbIndex, String key) {
        Jedis jedis = null;
        boolean isBroken = false;
        byte[] data = null;
        Object result = null;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            data = jedis.get(getBytesKey(key));
            if (data.length < 0 || data == null) {
                return null;
            }
            result = toObject(data);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getItem failed:", e);
        } finally {
            release(jedis, isBroken);
        }
        return result;
    }

    /**
     * 获取指定key的List
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public List<Object> getItemFromList(int dbIndex, String key) {
        Jedis jedis = null;
        boolean isBroken = false;
        List<Object> dataList = null;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            long len = jedis.llen(getBytesKey(key));
            if (len <= 0) {
                return null;
            }
            List<byte[]> byteList = jedis.lrange(getBytesKey(key), 0, (int) (len - 1));
            dataList = new ArrayList<Object>();
            for (byte[] b : byteList) {
                dataList.add(toObject(b));
            }

        } catch (Exception e) {
            isBroken = true;
            logger.error(" getItemFromList failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return dataList;
    }

    /**
     * 获取指定key的Map
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public Map<String, String> getMap(int dbIndex, String key) {
        Jedis jedis = null;
        boolean isBroken = false;
        Map<String, String> map = null;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            map = jedis.hgetAll(key);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getMap failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return map;
    }

    /**
     * 获取指定key的Map中的field
     *
     * @param dbIndex
     * @param key
     * @param field
     * @return
     */
    public String getMapItem(int dbIndex, String key, String field) {
        Jedis jedis = null;
        boolean isBroken = false;
        String result = null;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            result = jedis.hget(key, field);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getMapItem failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return result;
    }

    /**
     * 获取指定key的List
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public List<String> getList(int dbIndex, String key) {
        Jedis jedis = null;
        boolean isBroken = false;
        List<String> list = null;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            list = jedis.lrange(key, 0, -1);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getList failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return list;
    }

    /**
     * 获取指定key的Set
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public Set<String> getSet(int dbIndex, String key) {
        Jedis jedis = null;
        boolean isBroken = false;
        Set<String> set = null;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            set = jedis.smembers(key);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getSet failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return set;
    }


    /**
     * 获取指定key的byte[]
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public byte[] getItemByte(int dbIndex, String key) {
        Jedis jedis = null;
        boolean isBroken = false;
        byte[] bytes = null;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            bytes = jedis.get(getBytesKey(key));
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getSet failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return bytes;
    }


    /**
     * 获取指定key的String
     *
     * @param dbIndex
     * @param key
     */
    public String getString(int dbIndex, String key) {
        Jedis jedis = null;
        boolean isBroken = false;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            return jedis.get(key);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getSet failed:", e);
            return null;
        } finally {
            release(jedis, isBroken);
        }

    }

    /**
     * 指定key的String加1
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public long getIncr(int dbIndex, String key) {
        Jedis jedis = null;
        boolean isBroken = false;
        long result = 0;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            result = jedis.incr(key);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getIncr failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return result;
    }

    /**
     * 指定key的String加integer
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public long getIncrBy(int dbIndex, String key, long integer) {
        Jedis jedis = null;
        boolean isBroken = false;
        long result = 0;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            result = jedis.incrBy(key, integer);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getIncrBy failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return result;
    }

    /**
     * 指定key的String减1
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public long getDecr(int dbIndex, String key) {
        Jedis jedis = null;
        boolean isBroken = false;
        long result = 0;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            result = jedis.decr(key);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getDecr failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return result;
    }

    /**
     * 指定key的String减integer
     *
     * @param dbIndex
     * @param key
     * @return
     */
    public long getDecr(int dbIndex, String key, long integer) {
        Jedis jedis = null;
        boolean isBroken = false;
        long result = 0;
        try {
            jedis = getJedis();
            jedis.select(dbIndex);
            result = jedis.decrBy(key, integer);
        } catch (Exception e) {
            isBroken = true;
            logger.error(" getDecr failed:", e);
        } finally {
            release(jedis, isBroken);
        }

        return result;
    }


    /**
     * 获得jedis对象
     *
     * @return
     * @throws JedisException
     */
    private static Jedis getJedis() throws JedisException {
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
        } catch (JedisException e) {
            logger.error("failed:jedisPool getResource.", e);
            if (jedis != null) {
                pool.returnBrokenResource(jedis);
            }
        }
        return jedis;
    }

    /**
     * jedis销毁
     *
     * @param jedis
     * @param isBroken
     */
    private static void release(Jedis jedis, boolean isBroken) {
        if (jedis != null) {
            if (isBroken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    /**
     * 获取byte[]类型Key
     *
     * @param object
     * @return
     */
    public static byte[] getBytesKey(Object object) {
        if (object instanceof String) {
            return StrUtils.getBytes((String) object);
        } else {
            return ObjectUtils.serialize(object);
        }
    }

    /**
     * Object转换byte[]类型
     *
     * @param object
     * @return
     */
    public static byte[] toBytes(Object object) {
        return ObjectUtils.serialize(object);
    }

    /**
     * byte[]型转换Object
     *
     * @param bytes
     * @return
     */
    public static Object toObject(byte[] bytes) {
        return ObjectUtils.unserialize(bytes);
    }

}
